#pragma once
#include <iostream>
#include <cstdio>
#include <string>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

static const std::string defaultip = "0.0.0.0";
static const uint16_t defaultport = 8080;
static const int BUFSIZE = 1024;

class UdpServer
{
private:
    std::string _server_ip;
    uint16_t _server_port;
    int _sockfd;

public:
    UdpServer(const std::string& ip = defaultip, const uint16_t& port = defaultport)
        :_server_ip(ip), _server_port(port), _sockfd(-1) {}

    //初始化服务器
    void Init()
    {
        //创建通信套接字文件
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)
        {
            std::cerr << "create socket fail" << std::endl;
            std::abort();
        }
        //绑定ip和端口号
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(_server_port);
        //server.sin_addr.s_addr = inet_addr(_server_ip.c_str());
        inet_aton(_server_ip.c_str(), &(server.sin_addr));
        
        socklen_t len = sizeof(server);
        int bind_ret = bind(_sockfd, (struct sockaddr*)&server, len);
        if(bind_ret < 0)
        {
            std::cerr << "bind ip and port fail" << std::endl;
            std::abort();
        }

        //设置端口地址重用
        int val = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &val, sizeof(val));
    }

    //运行服务器
    void Run()
    {
        while(1)
        {
            //接收消息并保存client的ip和端口号
            char buf[BUFSIZE];
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            int ret = recvfrom(_sockfd, buf, BUFSIZE - 1, 0, (struct sockaddr*)&client, &len);
            if(ret < 0)
                continue;

            //解析client的ip和端口号
            const std::string clientip = inet_ntoa(client.sin_addr);
            const uint64_t clientport = ntohs(client.sin_port);

            //打印信息
            buf[ret] = '\0';
            printf("[%s:%d]# %s\n", clientip.c_str(), clientport);

            //回发数据
            sendto(_sockfd, buf, BUFSIZE, 0, (const sockaddr*)&client, len);
        }
    }
};